package math.exact;

import java.math.BigInteger;

public class Rational {

	private BigInteger numerator;
	private BigInteger denominator;

	public Rational(BigInteger numerator) {
		this.numerator = numerator;
		this.denominator = BigInteger.ONE;
	}

	public Rational(BigInteger numerator, BigInteger denominator) {
		if (denominator.equals(BigInteger.ZERO))
			throw new DenominatorMustNotBeZero();
		cancelAndAssignToFields(numerator, denominator);
		adjustSigns();
	}

	private void cancelAndAssignToFields(BigInteger num, BigInteger denom) {
		BigInteger gdc = num.gcd(denom);
		this.numerator = num.divide(gdc);
		this.denominator = denom.divide(gdc);
	}

	private void adjustSigns() {
		if (denominator.compareTo(BigInteger.ZERO) < 0) {
			numerator = numerator.negate();
			denominator = denominator.negate();
		}
	}

	@SuppressWarnings("serial")
	public class DenominatorMustNotBeZero extends RuntimeException {
	}

	@Override
	public boolean equals(Object other) {
		if (!(other instanceof Rational))
			return false;
		return numerator.equals(((Rational) other).numerator)
				&& denominator.equals(((Rational) other).denominator);
	}

	@Override
	public String toString() {
		String s = numerator.toString();
		if (!denominator.equals(BigInteger.ONE))
			s += "/" + denominator.toString();
		return s;
	}

	public Rational add(Rational x) {
		BigInteger thisExpanded = numerator.multiply(x.denominator);
		BigInteger otherExpanded = x.numerator.multiply(denominator);
		BigInteger commonDenominator = denominator.multiply(x.denominator);
		return new Rational(thisExpanded.add(otherExpanded), commonDenominator);
	}

	public Rational subtract(Rational x) {
		return add(new Rational(x.numerator.negate(), x.denominator));
	}

	public BigInteger getNumerator() {
		return numerator;
	}

	public BigInteger getDenominator() {
		return denominator;
	}

}
